package com.ys.http.network;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ys.http.api.ApiService;
import com.ys.http.download.DownloadTask;
import com.ys.http.download.OnDownloadListener;
import com.ys.http.entry.Resp;
import com.ys.http.upload.OnUploadListener;
import com.ys.http.upload.UploadTask;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;

import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.functions.Function;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;

public class HttpUtils {

    private static final ApiService API_SERVICE;

    static {
        API_SERVICE = HttpUtils.createApi(ApiService.class);
    }

    private static final Type<Resp<String>> RESP_TYPE_REFERENCE = new Type<Resp<String>>(){};

    public static <T> T createApi(Class<T> clazz) {
        return HttpHelper.getInstance()
                .createApi(clazz);
    }

    public static <T> T createApi(String baseUrl, Class<T> clazz) {
        return HttpHelper.getInstance()
                .changeBaseUrl(baseUrl)
                .createApi(clazz);
    }

    //get ↓↓↓

    /**
     *
     * @param url 请求的url
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void get(String url, Observer<T> resp) {
        get(url)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    /**
     *
     * @param url 请求的url
     * @param params 参数
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void get(String url, Params params, Observer<T> resp) {
        get(url, params)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    /**
     *
     * @param url 请求的url
     * @param header 请求头
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void get(String url, Header header, Observer<T> resp) {
        get(url, header)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    /**
     *
     * @param url 请求的url
     * @param params 参数
     * @param header 请求头参数
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void get(String url, Params params, Header header, Observer<T> resp) {
        get(url, params, header)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> Observable<T> get(String url, Class<T> clazz) {
        return get(url).map(json -> JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> get(String url, Type<T> type) {
        return get(url).map(json -> JSON.parseObject(json, type.getType()));
    }

    public static <T> Observable<T> get(String url, Params params, Class<T> clazz) {
        return get(url, params).map(json -> JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> get(String url, Params params, Type<T> type) {
        return get(url, params).map(json -> JSON.parseObject(json, type.getType()));
    }

    public static <T> Observable<T> get(String url, Params params, Header header, Class<T> clazz) {
        return get(url, params, header).map(json -> JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> get(String url, Params params, Header header, Type<T> type) {
        return get(url, params, header).map(json -> JSON.parseObject(json, type.getType()));
    }

    public static Observable<String> get(String url) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .get(url);
    }

    public static Observable<String> get(String url, Params params) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .get(url, params.get());
    }

    public static Observable<String> get(String url, Header header) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .headerGet(url, header.get());
    }

    public static Observable<String> get(String url, Params params, Header header) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .headerGet(url, params.get(), header.get());
    }

    //post ↓↓↓

    /**
     *
     * @param url 请求的url
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void post(String url, Observer<T> resp) {
        post(url)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    /**
     *
     * @param url 请求的url
     * @param body body参数 {@link com.ys.http.network.Body}
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void post(String url, Body body, Observer<T> resp) {
        post(url, body)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    /**
     *
     * @param url 请求的url
     * @param header 请求头
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void post(String url, Header header, Observer<T> resp) {
        post(url, header.get())
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    /**
     *
     * @param url 请求的url
     * @param body body参数 {@link com.ys.http.network.Body}
     * @param header 请求头参数
     * @param resp 回调
     * @param <T> 回调结果返回值类型
     */
    public static <T> void post(String url, Body body, Header header, Observer<T> resp) {
        post(url, body, header)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> void post(String url, Object bean, Observer<T> resp) {
        post(url, bean)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> void post(String url, Object bean, Header header, Observer<T> resp) {
        post(url, bean, header)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> void postJson(String url, Observer<T> resp) {
        post(url, JSON.parse("{}"))
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> void postJson(String url, Object bean, Observer<T> resp) {
        post(url, bean)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> void postJson(String url, Header header, Observer<T> resp) {
        post(url, JSON.parse("{}"), header)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> void postJson(String url, Object bean, Header header, Observer<T> resp) {
        post(url, bean, header)
                .map(HttpUtils.<T>createFunction(resp.getClass()))
                .compose(SchedulerProvider.applyScheduler())
                .subscribe(resp);
    }

    public static <T> Observable<T> post(String url, Class<T> clazz) {
        return post(url).map(json->JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> post(String url, Type<T> type) {
        return post(url).map(json->JSON.parseObject(json, type.getType()));
    }

    public static <T> Observable<T> post(String url, Body body, Class<T> clazz) {
        return post(url, body).map(json->JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> post(String url, Body body, Type<T> type) {
        return post(url, body).map(json->JSON.parseObject(json, type.getType()));
    }

    public static <T> Observable<T> post(String url,  Body body, Header header, Class<T> clazz) {
        return post(url,body, header).map(json->JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> post(String url, Body body, Header header, Type<T> type) {
        return post(url, body, header).map(json->JSON.parseObject(json, type.getType()));
    }

    public static <T> Observable<T> post(String url, Object bean, Class<T> clazz) {
        return post(url, bean).map(json->JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> post(String url, Object bean, Type<T> type) {
        return post(url, bean).map(json->JSON.parseObject(json, type.getType()));
    }

    public static <T> Observable<T> post(String url, Object bean, Header header, Class<T> clazz) {
        return post(url, bean, header).map(json->JSON.parseObject(json, clazz));
    }

    public static <T> Observable<T> post(String url, Object bean, Header header, Type<T> type) {
        return post(url, bean, header).map(json->JSON.parseObject(json, type.getType()));
    }

    private static <T> Function<String, T> createFunction(Class<?> clazz) {
        return json -> {
            ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
            if (parameterizedType != null) {
                java.lang.reflect.Type type =  parameterizedType.getActualTypeArguments()[0];
                if (parameterizedType.getRawType() == RespCallback.class) {
                    return JSON.parseObject(json, new TypeReference<Resp<T>>(type){}.getType());
                }
                if (parameterizedType.getRawType() == RespSubsCallback.class) {
                    return JSON.parseObject(json, new TypeReference<Resp<T>>(type){}.getType());
                }
                return JSON.parseObject(json, type);
            }
            return null;
        };
    }

    private static Observable<String> post(String url) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .post(url);
    }

    private static Observable<String> post(String url, Body body) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .post(url, body.body());
    }

    private static Observable<String> post(String url, Object bean) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .post(url, bean);
    }

    private static Observable<String> post(String url, Object bean, Header header) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .post(url, header.get(), bean);
    }

    private static Observable<String> post(String url, Body body, Header header) {
        return HttpHelper.getInstance()
                .createApi(ApiService.class)
                .post(url, header.get(), body.body());
    }

    //download ↓↓↓

    /**
     *
     * @param savePath 下载的文件保存路径
     * @param downloadUrl 下载链接
     * @param listener 下载监听
     * @return 下载器, 可用于停止下载任务
     */
    public static DownloadTask downloadFile(String savePath, String downloadUrl, OnDownloadListener listener) {
        DownloadTask task = new DownloadTask.Builder()
                .setSavePath(savePath)
                .setDownloadUrl(downloadUrl)
                .setOnDownloadListener(listener)
                .build();
        task.start();
        return task;
    }

    // upload ↓↓↓

    /**
     *
     * @param url 上传的url
     * @param file 需要上传的文件
     * @param listener 监听器
     * @return 用于停止
     */
    public static UploadTask uploadFile(String url, File file, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .setUploadUrl(url)
                .addParams(file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    public static UploadTask uploadFile(String url, Header header, File file, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .setUploadUrl(url)
                .addParams(file)
                .addHeader(header.get())
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    /**
     *
     * @param url 上传的url
     * @param name 根据后台的定义来定义此字段
     * @param file 需要上传的文件
     * @param listener 监听器
     * @return 用于停止
     */
    public static UploadTask uploadFile(String url, String name, File file, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .setUploadUrl(url)
                .addParams(name, file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    /**
     *
     * @param url 上传的url
     * @param header 请求头参数
     * @param name 根据后台的定义来定义此字段
     * @param file 需要上传的文件
     * @param listener 监听器
     * @return 用于停止
     */
    public static UploadTask uploadFile(String url, Header header, String name, File file, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .addHeader(header.get())
                .setUploadUrl(url)
                .addParams(name, file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    /**
     *
     * @param url 上传的url
     * @param params 请求参数
     * @param file 需要上传的文件
     * @param listener 监听器
     * @return 用于停止
     */
    public static UploadTask uploadFile(String url, File file, Params params, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .addParams(params.get())
                .setUploadUrl(url)
                .addParams(file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    public static UploadTask uploadFile(String url, File file, Params params, Header header, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .addHeader(header.get())
                .addParams(params.get())
                .setUploadUrl(url)
                .addParams(file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    public static UploadTask uploadFile(String url, String name, File file, Params params, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .addParams(params.get())
                .setUploadUrl(url)
                .addParams(name, file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }

    /**
     *
     * @param url 上传的url
     * @param params 请求参数
     * @param header 请求头参数
     * @param name 根据后台的定义来定义此字段
     * @param file 需要上传的文件
     * @param listener 监听器
     * @return 用于停止
     */
    public static UploadTask uploadFile(String url, String name, File file, Params params, Header header, OnUploadListener listener) {
        UploadTask task = new UploadTask.Builder()
                .addHeader(header.get())
                .addParams(params.get())
                .setUploadUrl(url)
                .addParams(name, file)
                .setOnUploadListener(listener)
                .build();
        task.start();
        return task;
    }


    //-----------------------------------------同步

    public static ResponseSync getSync(String url) {
        try {
            Response<ResponseBody> resp = API_SERVICE.getSync(url).execute();
            return new ResponseSync(resp);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseSync(e);
        }
    }

    public static ResponseSync postSync(String url) {
        try {
            Response<ResponseBody> resp = API_SERVICE.postSync(url).execute();
            return new ResponseSync(resp);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseSync(e);
        }
    }

    public static ResponseSync postSync(String url, Body body) {
        try {
            Response<ResponseBody> resp = API_SERVICE.postSync(url, body.body()).execute();
            return new ResponseSync(resp);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseSync(e);
        }
    }

    public static ResponseSync postSync(String url, Object bean) {
        try {
            Response<ResponseBody> resp = API_SERVICE.postSync(url, bean).execute();
            return new ResponseSync(resp);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseSync(e);
        }
    }

    public static ResponseSync postSync(String url, Object bean, Header header) {
        try {
            Response<ResponseBody> resp = API_SERVICE.postSync(url, header.get(), bean).execute();
            return new ResponseSync(resp);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseSync(e);
        }
    }

    public static ResponseSync postSync(String url, Body body, Header header) {
        try {
            Response<ResponseBody> resp = API_SERVICE.postSync(url, header.get(), body.body()).execute();
            return new ResponseSync(resp);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseSync(e);
        }
    }

}
